Skip to content

[中文版本](/posts/c教程/zh-CN/04-数据结构/034-Bitfields and Memory Packing)

C lets you control data layout down to the bit level using bitfields. They allow you to store small values compactly inside a struct, perfect for flags, configuration registers, or communication protocols. Combined with packing, you can squeeze data into minimal space while still keeping it easy to manipulate symbolically.

What Is a Bitfield? ​

A bitfield lets you define the exact number of bits to allocate for a field inside astruct.

Example:

struct Flags {
    unsigned int is_visible : 1;
    unsigned int is_enabled : 1;
    unsigned int has_error  : 1;
};

Here, each field uses just 1 bit instead of a full 4-byteint. That means 8 such flags fit comfortably in one byte.

Declaring and Using Bitfields ​

#include <stdio.h>
struct Status {
    unsigned int connected : 1;
    unsigned int error     : 1;
    unsigned int active    : 1;
    unsigned int reserved  : 5; // padding bits
};
int main(void) {
    struct Status s = {1, 0, 1, 0};
    printf("Connected: %u, Active: %u\n", s.connected, s.active);
    s.error = 1;
    printf("Error now: %u\n", s.error);
    return 0;
}

Output:

Connected: 1, Active: 1
Error now: 1

Even though there are 4 fields, the entire struct typically occupies only 1 byte.

Tiny Code ​

Here’s a complete example demonstrating packed flags and printing bit values:

#include <stdio.h>
struct DeviceStatus {
    unsigned int powered_on : 1;
    unsigned int connected  : 1;
    unsigned int has_error  : 1;
    unsigned int battery_low: 1;
    unsigned int reserved   : 4;
};
void print_bits(unsigned char byte) {
    for (int i = 7; i >= 0; i--)
        printf("%d", (byte >> i) & 1);
    printf("\n");
}
int main(void) {
    struct DeviceStatus d = {1, 1, 0, 0, 0};
    printf("Size of DeviceStatus: %zu bytes\n", sizeof(d));
    unsigned char *raw = (unsigned char*)&d;
    printf("Binary layout: ");
    print_bits(*raw);
    d.has_error = 1;
    printf("Updated binary: ");
    print_bits(*raw);
    return 0;
}

Output (may vary by platform):

Size of DeviceStatus: 1 bytes
Binary layout: 00000011
Updated binary: 00000111

Nested Bitfields Example ​

You can even use bitfields inside nested structs to create compact yet expressive data models:

struct Sensor {
    unsigned id       : 4;  // 0–15
    unsigned type     : 3;  // 0–7
    unsigned active   : 1;  // boolean
};
struct Device {
    struct Sensor sensors[2];
};

Each sensor entry now fits neatly into a single byte.

Memory Packing ​

By default, compilers may insert padding bytes to align fields for faster access. If you want tighter packing, for example, when saving binary data to a file or sending over a network, you can request packed structs.

Compiler directives differ by system:

#pragma pack(push, 1)
struct Packet {
    char type;
    unsigned int length;
    short checksum;
};
#pragma pack(pop)

Now the struct is tightly packed without alignment padding between fields.

Bitfields in Real Systems ​

Bitfields are used everywhere in systems programming:

  • Hardware control registers: Represent on/off bits for devices.
  • Network protocols: Flags in TCP, UDP, or IP headers.
  • Compression and serialization: Compact representation of status or metadata.
  • Embedded systems: Save every byte of RAM in microcontrollers.

Limitations ​

  • Bitfield ordering (which bit is “first”) is implementation-dependent, varies by compiler and platform.
  • They cannot cross word boundaries reliably across architectures.
  • Bitfields cannot be directly addressed by pointers (&field not allowed).
  • Endianness matters if transmitting across different systems.

For portable bit-level control (especially in networking), many engineers use explicit bitwise operators instead.

Manual Bitwise Control ​

Sometimes you’ll prefer manual masks and shifts:

unsigned char flags = 0;
flags |= (1 << 0); // set bit 0
flags |= (1 << 2); // set bit 2
flags &= ~(1 << 0); // clear bit 0

This approach is more portable and explicit, but less readable for large sets of flags.

Why It Matters ​

Bitfields give you compact control over memory layout and binary representation. They’re critical in:

  • Embedded firmware
  • Network stacks
  • Kernel drivers
  • Compression libraries

They make your code expressive and efficient, as long as you understand alignment and portability issues.

Try It Yourself ​

  1. Define a structPermissions with 1-bit fields forread,write, andexecute.
  2. Print its size and check how compact it is.
  3. Use a bitfield struct to represent a simplified TCP header (flags like SYN, ACK, FIN).
  4. Use#pragma pack(1) and observe the size difference.
  5. Write functions to set, clear, and toggle bits using bitwise operators.

Bitfields are where C meets the hardware. They let you talk to the machine not just in bytes, but in bits, the true language of computers. Next, you’ll revisit enumerations and see how they complement these compact structures by giving symbolic meaning to values.

本站总访问 — 次